home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / Pascal / Applications / NIH Image 1.62b11 / src / Registration.p < prev    next >
Encoding:
Text File  |  1996-03-01  |  29.1 KB  |  636 lines  |  [TEXT/CWIE]

  1. unit Registration;
  2. {************************************************************************}
  3. {*     Registration.p                                                                                                                                       *}
  4. {*     by Michael Castle                                                                                                                                  *}
  5. {*     University of Michigan Mental Health Research Institute (MHRI)                                                     *}
  6. {*     e-mail address: mike.castle@umich.edu                                                                                       *}
  7. {*     last modified on 11 March 1994                                                                                                          *}
  8. {************************************************************************}
  9.  
  10. interface
  11.  
  12.     uses
  13.         Types, Memory, QuickDraw, QuickDrawText, Packages, Menus, Events, Fonts, 
  14.         Scrap, ToolUtils, Resources, Errors, Palettes, StandardFile, Windows, OSUtils,
  15.         Controls, TextEdit, Files, Dialogs, TextUtils, Finder, MixedMode, Processes,
  16.         Globals, Utilities, File2, File1, Graphics, Camera, Text, Filters, Stacks;
  17.  
  18.  
  19.     procedure DoRegister;
  20.  
  21.  
  22. implementation
  23.  
  24.     const
  25.  
  26.         RegisterImagesID = 129;                         {Dialog IDs}
  27.         FiducialsOnScreenID = 3;
  28.         FiducialsFromFileID = 4;
  29.         ConfirmFidClicksID = 5;
  30.  
  31.         biggestFid = 9999;                                    {maximum allowable fiducial stage coordinate}
  32.         MaxFids = 12;                                           {maximum number of fiducial points per slice}
  33.         MaxRegSlices = 250;
  34.  
  35.     type
  36.         RegisterRealArray = array[1..MaxRegSlices] of real;
  37.         RealPoint = record
  38.                 x: real;
  39.                 y: real;
  40.             end;     {record}
  41.         FidArray = array[1..MaxRegSlices, 1..MaxFids] of RealPoint;
  42.  
  43.  
  44. {******************************************************************************}
  45. {*     RotateAboutXY rotates the point (x,y) counterclockwise by 'angle' radians about the point (xcenter,           *}
  46. {*  ycenter).                                                                                                                                                                *}
  47. {******************************************************************************}
  48.     procedure RotateAboutXY (var x, y: extended; angle: extended; xcenter, ycenter: extended);
  49.         var
  50.             x0, y0: extended;
  51.             SinAngle, CosAngle: extended;
  52.     begin
  53.         x0 := x;
  54.         y0 := y;
  55.         CosAngle := cos(angle);
  56.         SinAngle := sin(angle);
  57.         x := (x0 - xcenter) * CosAngle - (y0 - ycenter) * SinAngle + xcenter;
  58.         y := (y0 - ycenter) * CosAngle + (x0 - xcenter) * SinAngle + ycenter;
  59.     end;     {RotateAboutXY}
  60.  
  61.  
  62. {******************************************************************************}
  63. {*     Read from a file the fiducial data necessary to register a set of images.  The data file contains several lines *}
  64. {*  of coordinates delimited by tabs (x-coordinate followed by y-coordinate in all cases).  The first line of the   *}
  65. {*  file should hold the coordinates of the Image Center point, the location (in screen coordinates) of the             *}
  66. {*  microscope crosshairs as they appear on the screen during image capture.  The second line of the file should *}
  67. {*  give the location in screen coordinates of two fixed points in an image (under the camera and microscope       *}
  68. {*  conditions selected for capture of the set of images to be aligned).  The third line of the file should provide    *}
  69. {*  the location of these two fixed points in microscope stage coordinates.  Each subsequent line in the file           *}
  70. {*  should contain (in stage coordinates) the locations of the Image Center and at least two fiducial points for an  *}
  71. {*  image in the set to be registered.  Each image must be represented by the same number of fiducial points in   *}
  72. {*  the data file.  Where fiducial coordinates are unavailable, coordinates of biggestFid+1 should appear.  No      *}
  73. {*  more than MaxFids fiducial points are allowed for each image.                                                                           *}
  74. {******************************************************************************}
  75.     function GetFiducialDataFromFile (var xcscreen, ycscreen, xscreen1, yscreen1, xscreen2, yscreen2: integer; var xstage1, ystage1, xstage2, ystage2: extended; var fiducials: FidArray; var xc, yc: RegisterRealArray): boolean;
  76.         var
  77.             fiducialfname, str: str255;
  78.             RefNum, nValues, i, j, nImages: integer;
  79.             rLine: RealLine;
  80.     begin
  81.         nImages := info^.StackInfo^.nSlices;
  82.         GetFiducialDataFromFile := FALSE;
  83.         ShowMessage('Please open a fiducial data file.');
  84.         if not GetTextFile(fiducialfname, RefNum) then
  85.             exit(GetFiducialDataFromFile);
  86.         InitTextInput(fiducialfname, RefNum);
  87.         GetLineFromText(rLine, nValues);
  88.         if (nValues <> 2) then begin
  89.                 PutError('Expecting screen coordinates of Image Center point in line 1.  Please edit fiducial data file and try again.');
  90.                 exit(GetFiducialDataFromFile);
  91.             end;
  92.         xcscreen := round(rLine[1]);
  93.         ycscreen := round(rLine[2]);
  94.         GetLineFromText(rLine, nValues);
  95.         if (nValues <> 4) then begin
  96.                 PutError('Expecting screen coordinates of two points in line 2.  Please edit fiducial data file and try again.');
  97.                 exit(GetFiducialDataFromFile);
  98.             end;
  99.         xscreen1 := round(rLine[1]);
  100.         yscreen1 := round(rLine[2]);
  101.         xscreen2 := round(rLine[3]);
  102.         yscreen2 := round(rLine[4]);
  103.         GetLineFromText(rLine, nValues);
  104.         if (nValues <> 4) then begin
  105.                 PutError('Expecting stage coordinates of two points in line 3.  Please edit fiducial data file and try again.');
  106.                 exit(GetFiducialDataFromFile);
  107.             end;
  108.         xstage1 := rLine[1];
  109.         ystage1 := rLine[2];
  110.         xstage2 := rLine[3];
  111.         ystage2 := rLine[4];
  112.         i := 1;
  113.         GetLineFromText(rLine, nValues);
  114.         while (nvalues > 0) do begin
  115.                 if nValues >= 6 then begin
  116.                         for j := 1 to (nvalues - 2) div 2 do begin
  117.                                 fiducials[i, j].x := rLine[j * 2 + 1];
  118.                                 fiducials[i, j].y := rLine[j * 2 + 2];
  119.                             end;
  120.                         for j := (nvalues - 2) div 2 + 1 to MaxFids do begin
  121.                                 fiducials[i, j].x := biggestFid + 1;
  122.                                 fiducials[i, j].y := biggestFid + 1;
  123.                             end;     {for j}
  124.                         xc[i] := rLine[1];
  125.                         yc[i] := rLine[2];
  126.                     end
  127.                 else begin
  128.                         str := StringOf('Expecting coordinates of image center and at least two fiducial points in line ', (i + 3) : 1, '.  Please edit fiducial data file and try again.');
  129.                         PutError(str);
  130.                         exit(GetFiducialDataFromFile);
  131.                     end;
  132.                 i := i + 1;
  133.                 GetLineFromText(rLine, nValues);
  134.             end;     {while}
  135.         if (i < (nImages + 1)) then begin
  136.                 if (i = nImages) then
  137.                     str := StringOf('Expecting fiducial data for one more image.  Please edit fiducial data file, then try again. ')
  138.                 else
  139.                     str := StringOf('Expecting fiducial data for ', (nImages + 1 - i) : 1, ' more slices.  Please edit fiducial data file, then try again. ');
  140.                 PutError(str);
  141.                 exit(GetFiducialDataFromFile);
  142.             end;
  143.         if (i > (nImages + 1)) then begin
  144.                 if (i = nImages + 2) then
  145.                     str := StringOf('Too much fiducial data.  Please edit fiducial data file, then try again. ')
  146.                 else
  147.                     str := StringOf('Too much fiducial data.  Please edit fiducial data file, then try again. ');
  148.                 PutError(str);
  149.                 exit(GetFiducialDataFromFile);
  150.             end;
  151.         GetFiducialDataFromFile := TRUE;
  152.     end;     {GetFiducialDataFromFile}
  153.  
  154.  
  155. {******************************************************************************}
  156. {*     Read the coordinates of a fiducial point entered on the screen by clicking once with the mouse.  Interpret    *}
  157. {*  a double-click to indicate that this is the last fiducial point for the current slice, a spacebar-click to          *}
  158. {*  to indicate that no valid fiducial exists corresponding to this fiducial in other slices (record BiggestFid+1   *}
  159. {*  as value for each coordinate), an option-click to indicate that some data for this slice has been improperly  *}
  160. {*  and the user would like to re-enter all fiducials for this slice, and command-period to indicate that the       *}
  161. {*  user wishes to cancel image registration altogether, discarding all entered fiducials coordinates.                    *}
  162. {******************************************************************************}
  163.     function GetNextFiducial (var Fidx, Fidy: integer; var done, redo: boolean): boolean;
  164.         var
  165.             pt1, pt2: point;
  166.             sbdown, optdown, DoubleClick: boolean;
  167.             MouseUpTime: LongInt;
  168.     begin
  169.         SetCursor(ToolCursor[SelectionTool]);
  170.         GetNextFiducial := FALSE;
  171.         repeat
  172.             sbdown := SpaceBarDown;
  173.             optdown := OptionKeyDown;
  174.             SetPort(info^.wptr);
  175.             GetMouse(pt1);
  176.             Show3Values(pt1.h, pt1.v, MyGetPixel(pt1.h, pt1.v));
  177.             if CommandPeriod then begin
  178.                     ShowMessage('Fiducial input cancelled.');
  179.                     exit(GetNextFiducial);
  180.                 end;     {then}
  181.         until button;
  182.         repeat
  183.         until not (button);
  184.         MouseUpTime := TickCount;
  185.         DoubleClick := FALSE;
  186.         repeat
  187.             GetMouse(pt2);
  188.             if EqualPt(pt1, pt2) then
  189.                 DoubleClick := button;
  190.         until (TickCount - MouseUpTime > GetDblTime) or DoubleClick;
  191.         if sbdown then begin
  192.                 Fidx := BiggestFid + 1;
  193.                 Fidy := BiggestFid + 1;
  194.             end
  195.         else if optdown then
  196.             redo := TRUE
  197.         else begin
  198.                 Fidx := pt1.h;
  199.                 Fidy := (Info^.nLines - 1) - pt1.v;
  200.             end;
  201.         done := DoubleClick;
  202.         while (button) do                    {clear out any buffered mouse clicks;  I don't know why there would be any}
  203.             ;                                               {such clicks, but they can be *very* disruptive while running on Quadras!}
  204.         FlushEvents(62, 0);                {make sure clicks and key presses don't linger in the event queue after exit}
  205.         GetNextFiducial := TRUE;
  206.     end;     {GetNextFiducial}
  207.  
  208.  
  209.     procedure SetSlice (i: integer);
  210.     begin
  211.         SelectSlice(i);
  212.         Info^.StackInfo^.CurrentSlice := i;
  213.         UpdateTitleBar;
  214.     end;
  215.  
  216.  
  217. {******************************************************************************}
  218. {*     Read fiducial coordinates for a set of slices to be placed in register with a reference slice (a member of   *}
  219. {*  the set).  Begin by reading fiducials for the reference slice, then read the rest.  Assign dummy values to      *}
  220. {*  variables in the fiducial data structure whose values would be used for mapping between two coordinate        *}
  221. {*  systems had fiducial data been read from a file.                                                                                                   *}
  222. {******************************************************************************}
  223.     function GetFiducialDataFromScreen (var xcscreen, ycscreen, xscreen1, yscreen1, xscreen2, yscreen2: integer; var xstage1, ystage1, xstage2, ystage2: extended; var fiducials: FidArray; var xc, yc: RegisterRealArray): boolean;
  224.         var
  225.             i, j: integer;
  226.             nImages: integer;                               {number of slices}
  227.             RefSlice: integer;                               {index of the reference slice}
  228.             Fidx, Fidy: integer;                            {coordinates of selected fiducial point}
  229.             done: boolean;                                     {done entering fiducials for this slice}
  230.             redo: boolean;                                     {re-enter fiducials for this slice}
  231.             str: str255;
  232.     begin
  233.         GetFiducialDataFromScreen := FALSE;
  234.         nImages := Info^.StackInfo^.nSlices;
  235.         xcscreen := 100;                                 {arbitrarily assign dummy image center}
  236.         ycscreen := 100;
  237.         xscreen1 := 50;                                   {assign screen and stage coords of two points}
  238.         yscreen1 := 50;                                   {so that mapping is 1:1 in x and y}
  239.         xscreen2 := 80;
  240.         yscreen2 := 80;
  241.         xstage1 := 50.0;
  242.         ystage1 := 50.0;
  243.         xstage2 := 80.0;
  244.         ystage2 := 80.0;
  245.         DrawLabels('X: ', 'Y: ', 'Value: ');        {prepare to show x,y values in results window}
  246.         RefSlice := info^.StackInfo^.CurrentSlice;
  247.         i := RefSlice;                                   {begin with reference slice}
  248.         while (i <= nImages) do begin
  249.                 done := FALSE;
  250.                 redo := FALSE;
  251.                 SetSlice(i);
  252.                 UpdatePicWindow;
  253.                 for j := 1 to MaxFids do begin
  254.                         if (not done) and (not redo) then begin
  255.                                 str := StringOf('Click on fiducial point  ', j : 1);
  256.                                 showmessage(str);
  257.                                 if not GetNextFiducial(Fidx, Fidy, done, redo) then
  258.                                     exit(GetFiducialDataFromScreen);
  259.                                 if ConfirmFidClicks then begin
  260.                                         str := StringOf('Fiducial point ', j : 1, ':  x = ', Fidx : 1, '  y = ', Fidy : 1);
  261.                                         if not (redo) then
  262.                                             while (PutMessageWithCancel(str) = cancel) do begin
  263.                                                     UpdatePicWindow;
  264.                                                     if not GetNextFiducial(Fidx, Fidy, done, redo) then
  265.                                                         exit(GetFiducialDataFromScreen);
  266.                                                     str := StringOf('Fiducial point ', j : 1, ':  x = ', Fidx : 1, '  y = ', Fidy : 1);
  267.                                                 end;     {while}
  268.                                         UpdatePicWindow;
  269.                                     end;     {then}
  270.                                 if done and (j = 1) then begin
  271.                                         PutError('You must select at least two fiducial points in each slice for registration.');
  272.                                         redo := TRUE;
  273.                                     end;     {then}
  274.                                 fiducials[i, j].x := Fidx;
  275.                                 fiducials[i, j].y := Fidy;
  276.                             end    {then}
  277.                         else begin                                  {pad rest of array with invalid fiducial data}
  278.                                 fiducials[i, j].x := biggestFid + 1;
  279.                                 fiducials[i, j].y := biggestFid + 1;
  280.                             end;     {else}
  281.                     end;     {for j}
  282.                 xc[i] := 100;
  283.                 yc[i] := 100;
  284.                 if not (redo) then begin
  285.                         if (i = RefSlice) and (RefSlice <> 1) then
  286.                             i := 1
  287.                         else if (i = RefSlice - 1) then  {don't read fiducials from reference slice twice!}
  288.                             i := i + 2
  289.                         else
  290.                             i := i + 1;
  291.                     end     {then}
  292.                 else
  293.                     PutError('Input cancelled.  Please reselect fiducial points for this slice.');
  294.             end;     {while}
  295.         GetFiducialDataFromScreen := TRUE;
  296.     end;     {GetFiducialDataFromScreen}
  297.  
  298.  
  299. {******************************************************************************}
  300. {*     Before rotating and translating an image into register, center it in a larger buffer so that rotation does      *}
  301. {*  not unnecessarily clip portions of the image that will return to view after translation.                                   *}
  302. {******************************************************************************}
  303.     procedure CenterInBigBuffer (i, picwidth, picheight, bigpicwidth, bigpicheight: integer; StackInfo: InfoPtr);
  304.         var
  305.             vloc, hOffset, vOffset: integer;
  306.             BigBufInfo: InfoPtr;
  307.             aLine: LineType;
  308.     begin
  309.         BigBufInfo := info;
  310.         info := StackInfo;
  311.         SetSlice(i);
  312.         info := BigBufInfo;
  313.         SetForegroundColor(BlackIndex);
  314.         SetBackgroundColor(WhiteIndex);
  315.         SelectAll(false);
  316.         DoOperation(EraseOp);
  317.       {write image one line at a time to center of larger buffer}
  318.         hOffset := (bigpicwidth - picwidth) div 2;
  319.         vOffset := (bigpicheight - picheight) div 2;
  320.         for vloc := 0 to picheight - 1 do begin
  321.                 info := StackInfo;
  322.                 GetLine(0, vloc, picwidth, aLine);
  323.                 info := BigBufInfo;
  324.                 PutLine(hOffset, vloc + vOffset, picwidth, aLine);
  325.             end;
  326.         UpdatePicWindow;
  327.     end;     {RegisterCenterInBigBuffer}
  328.  
  329.  
  330. {******************************************************************************}
  331. {*     After registration is complete, move the centered image back to its original window size.                             *}
  332. {******************************************************************************}
  333.     procedure TranslateBackToStack (i, xdelta, ydelta, picwidth, picheight, bigpicwidth, bigpicheight: longint; StackInfo: InfoPtr);
  334.         var
  335.             vloc, hoffset, voffset: integer;
  336.             offset: longint;
  337.             BigBufInfo: InfoPtr;
  338.             aLine: LineType;
  339.     begin
  340.         BigBufInfo := info;
  341.         info := StackInfo;
  342.         SetSlice(i);
  343.         hOffset := (bigpicwidth - picwidth) div 2 - xdelta;
  344.         vOffset := (bigpicheight - picheight) div 2 + ydelta;
  345.         for vloc := 0 to picheight - 1 do begin
  346.                 info := BigBufInfo;
  347.                 GetLine(hOffset, vloc + vOffset, picwidth, aLine);
  348.                 info := StackInfo;
  349.                 PutLine(0, vloc, picwidth, aLine);
  350.             end;
  351.         UpdatePicWindow;
  352.         info := BigBufInfo;
  353.     end;     {RegisterBackToSmallWindow}
  354.  
  355.  
  356. {******************************************************************************}
  357. {*     Find the angle which the current slice must be rotated in order to place it in register with the reference  *}
  358. {*  slice.  For corresponding pairs of fiducial points in the current and reference slices, use simple             *}
  359. {*  trigonometry to find the angle between lines passing through each pair of points.  Take an everage of the      *}
  360. {*  angles found from each set of corresponding pairs of points to find the rotation angle.                        *}
  361. {******************************************************************************}
  362.     function RegisterFindAngle (var fiducials: FidArray; Cur, Ref: integer): extended;
  363.         var
  364.             j, k, n: integer;
  365.             angle, anglecur, angleref: extended;
  366.             tancur, tanref, bfid: extended;
  367.             sumangle: extended;
  368.             b:array[1..9] of boolean;
  369.     begin
  370. {find average angle between current fiducial segments and reference fiducial segments}
  371.         sumangle := 0;
  372.         n := 0;
  373.         for j := 1 to MaxFids - 1 do
  374.             for k := j + 1 to MaxFids do begin
  375.                 bfid:=biggestFid; {ppc-bug}
  376.                 if (j <> k) and (fiducials[Cur, j].x < bfid) and (fiducials[Cur, j].y < bfid) and (fiducials[Cur, k].x < bfid)
  377.                 and (fiducials[Cur, k].y < bfid) and (fiducials[Ref, j].x < bfid) and (fiducials[Ref, j].y < bfid)
  378.                 and (fiducials[Ref, k].x < bfid) and (fiducials[Ref, k].y < bfid) then begin
  379.                         tanref := (fiducials[Ref, k].y - fiducials[Ref, j].y) / (fiducials[Ref, k].x - fiducials[Ref, j].x);
  380.                         if ((tanref > 0) and (fiducials[Ref, k].y - fiducials[Ref, j].y < 0)) or ((tanref < 0) and (fiducials[Ref, k].y - fiducials[Ref, j].y > 0)) then
  381.                             angleref := arctan(tanref) + pi
  382.                         else
  383.                             angleref := arctan(tanref);
  384.                         tancur := (fiducials[Cur, k].y - fiducials[Cur, j].y) / (fiducials[Cur, k].x - fiducials[Cur, j].x);
  385.                         if ((tancur > 0) and (fiducials[Cur, k].y - fiducials[Cur, j].y < 0)) or ((tancur < 0) and (fiducials[Cur, k].y - fiducials[Cur, j].y > 0)) then
  386.                             anglecur := arctan(tancur) + pi
  387.                         else
  388.                             anglecur := arctan(tancur);
  389.                         angle := anglecur - angleref;
  390.                         if (angle > pi) then
  391.                             angle := angle - 2 * pi;
  392.                         if (angle <= -pi) then
  393.                             angle := angle + 2 * pi;
  394.                         sumangle := sumangle + angle;
  395.                         n := n + 1;
  396.                     end;     {then}
  397.                 end;
  398.         if (n > 0) then
  399.             RegisterFindAngle := sumangle / n
  400.         else begin
  401.                 PutError('Insufficient fiducial data to calculate registration rotation.');
  402.                 RegisterFindAngle := 10000;
  403.             end;     {else}
  404.     end;     {function RegisterFindAngle}
  405.  
  406.  
  407. {*************************************}
  408. {*  Rotates the slice using ScaleAndRotate routine.     *}
  409. {*************************************}
  410.     procedure RegisterRotate (AngleInRadians: extended);
  411.     begin
  412.         rsHScale := 1.0;
  413.         rsVScale := 1.0;
  414.         rsAngle := (AngleInRadians / pi) * 180.0;
  415.         if info^.LutMode = ColorLut then
  416.             rsMethod := NearestNeighbor
  417.         else
  418.             rsMethod := Bilinear;
  419.         rsCreateNewWindow := false;
  420.         macro := true; {So ScaleAndRotate won't display its dialog box.}
  421.         ScaleAndRotate;
  422.         Macro := false;
  423.     end;
  424.  
  425.  
  426. {******************************************************************************}
  427. {*     Find the distances in x and y which the current slice must be translated in order to place it in register     *}
  428. {*  with the reference slice.                                                                                                                                      *}
  429. {******************************************************************************}
  430.     procedure FindTranslation (var xdelta, ydelta: integer; var fiducials: FidArray; i, RefPic: integer; xxscale, yyscale, angle: extended; xc, yc: RegisterRealArray; xcenterStage, ycenterStage: extended);
  431.         var
  432.             xcur, ycur: extended;
  433.             xdeltaindex: extended;     {used to detect non-linear mapping between coordinate systems of current}
  434.             ydeltaindex: extended;     {     and reference slices;  the closer to zero, the more linear the mapping}
  435.             xdeltamin, ydeltamin: extended; {minimize indices to find best fiducials for translation calculations}
  436.             j: integer;
  437.             bfid: extended;
  438.     begin
  439.         xdeltamin := biggestFid;
  440.         ydeltamin := biggestFid;
  441.         bfid := biggestFid;  {ppc-bug}
  442.         for j := 1 to MaxFids do
  443.             if (fiducials[i, j].x < bfid) and (fiducials[i, j].y < bfid) then begin
  444.                     xcur := fiducials[i, j].x - xc[i];
  445.                     ycur := fiducials[i, j].y - yc[i];
  446. {rotate original fiducials about screen center}
  447. {this changes values of first two parameters on return}
  448.                     RotateAboutXY(xcur, ycur, -angle, xcenterStage, ycenterStage);
  449. {calculate translation offsets}
  450.                     xdeltaindex := abs(fiducials[i, j].x - xc[i]) + abs(fiducials[RefPic, j].x - xc[RefPic]);
  451.                     if (xdeltaindex < xdeltamin) then begin     {try to minimize effect of warped tissue}
  452.                             xdelta := round(xxscale * (fiducials[RefPic, j].x - xc[RefPic] - xcur));
  453.                             xdeltamin := xdeltaindex;
  454.                         end;     {then}
  455.                     ydeltaindex := abs(fiducials[i, j].y - yc[i]) + abs(fiducials[RefPic, j].y - yc[RefPic]);
  456.                     if (ydeltaindex < ydeltamin) then begin     {try to minimize effect of warped tissue}
  457.                             ydelta := round(yyscale * (fiducials[RefPic, j].y - yc[RefPic] - ycur));
  458.                             ydeltamin := ydeltaindex;
  459.                         end;     {then}
  460.                 end;     {then}
  461.     end;     {procedure RegisterFindTranslation}
  462.  
  463.  
  464.  
  465. {******************************************************************************}
  466. {*     This procedure allows the user to determine, via radio buttons in a dialog box, whether fiducial data           *}
  467. {*  will be read from the screen or from a file.  The dialog box also contains details about how to select fiducial   *}
  468. {*  points on the screen.                                                                                                                                              *}
  469. {******************************************************************************}
  470.     function RegisterOptions: boolean;
  471.         var
  472.             mylog: Dialogptr;                                           {pointer to dialog box}
  473.             i, item, alldone: integer;
  474.             SaveFiducialMethod: FiducialMethodType;
  475.             SaveConfirmFidClicks: boolean;
  476.     begin
  477.         RegisterOptions := FALSE;
  478.         InitCursor;
  479.         SaveConfirmFidClicks := ConfirmFidClicks;
  480.         SaveFiducialMethod := FiducialMethod;
  481.         mylog := GetNewDialog(RegisterImagesID, nil, pointer(-1));
  482.         OutlineButton(MyLog, ok, 16);
  483.         SetDlogItem(MyLog, FiducialsOnScreenID, ord(FiducialMethod = OnScreen));
  484.         SetDlogItem(MyLog, FiducialsFromFileID, ord(FiducialMethod = FromFile));
  485.         SetDlogItem(MyLog, ConfirmFidClicksID, ord(ConfirmFidClicks));
  486.         alldone := 0;
  487.         repeat  {if we don't do it this way, ModalDialog throws us into code checking after each keystroke}
  488.             repeat   {meaning you can't type in a 2 digit number}
  489.                 ModalDialog(nil, item);
  490.                 if item = ConfirmFidClicksID then begin
  491.                         ConfirmFidClicks := not ConfirmFidClicks;
  492.                         SetDlogItem(MyLog, ConfirmFidClicksID, ord(ConfirmFidClicks));
  493.                     end;
  494.                 if (item = FiducialsOnScreenID) or (item = FiducialsFromFileID) then begin
  495.                         case item of
  496.                             FiducialsOnScreenID: 
  497.                                 FiducialMethod := OnScreen;
  498.                             FiducialsFromFileID: 
  499.                                 FiducialMethod := FromFile;
  500.                         end;     {case}
  501.                         SetDlogItem(MyLog, FiducialsOnScreenID, ord(FiducialMethod = OnScreen));
  502.                         SetDlogItem(MyLog, FiducialsFromFileID, ord(FiducialMethod = FromFile));
  503.                     end;
  504.             until (item = ok) or (item = cancel);
  505.             alldone := 1;
  506.         until (alldone = 1);
  507.         DisposeDialog(mylog);
  508.         if item = cancel then begin                             {if Cancel, keep the saved values}
  509.                 FiducialMethod := SaveFiducialMethod;
  510.                 ConfirmFidClicks := SaveConfirmFidClicks;
  511.                 Exit(RegisterOptions);
  512.             end;
  513.         RegisterOptions := TRUE;
  514.     end;
  515.  
  516.  
  517.  
  518. {******************************************************************************}
  519. {*     Place a set of slices in register with a reference slice using fiducial marks.  All slices in the stack              *}
  520. {*  are placed in register with the current slice using fiducial data gathered either from a text file or                 *}
  521. {*  from the user's mouse clicks on the screen.                                                                                                         *}
  522. {******************************************************************************}
  523.     procedure DoRegister;
  524.         var
  525.             nImages: integer;                                                     {total number of open slices to register}
  526.             RefImage: integer;                                                       {the index of the reference slice}
  527.             bigpicwidth, bigpicheight: longint;                        {width,height of big buffer used for rotation and translation}
  528.             picwidth, picheight: integer;                                  {width,height of slices to register}
  529.             xcenter, ycenter: integer;                                      {coordinates of center of big, temp window}
  530.             xdelta, ydelta: integer;                                            {translation offsets}
  531.             xcscreen, ycscreen: integer;                                  {image center on the screen}
  532.             xscreen1, yscreen1, xscreen2, yscreen2: integer;{two points in screen coordinates}
  533.             xstage1, ystage1, xstage2, ystage2: extended;              {same two points in stage coordinates}
  534.             xxscale, yyscale: extended;                                               {used for mapping stage to screen coords}
  535.             xc, yc: RegisterRealArray;                                    {array of image centers in stage coords}
  536.             fiducials: FidArray;                                                {array of fiducial point data for all slices}
  537.             xcenterStage, ycenterStage: extended;                          {used in translation calculation}
  538.             angle: extended;                                                             {mean angle between ref and cur fid segments}
  539.             i, ignore: integer;                                                   {loop indices and temp var}
  540.             TimeStr, seconds: str255;
  541.             StartTicks, TicksForOneRegistration, TicksToGo: LongInt;
  542.             StackInfo: InfoPtr;
  543.             SlicesDone: integer;
  544.  
  545.     begin
  546.         if not (RegisterOptions) then
  547.             exit(DoRegister);
  548.         with Info^ do begin
  549.                 picwidth := PixelsPerLine;
  550.                 picheight := nLines;
  551.                 RefImage := StackInfo^.CurrentSlice;
  552.                 nImages := StackInfo^.nSlices;
  553.             end;
  554.         if nImages > MaxRegSlices then begin
  555.                 PutError(concat('Unable to register more than ', long2str(MaxRegSlices), ' slices.'));
  556.                 exit(DoRegister);
  557.             end;
  558.         StackInfo := info;
  559.         bigpicwidth := 2 * (round(1.414 * picwidth) div 2);      {allow for image rotation without losing corners}
  560.         bigpicheight := 2 * (round(1.414 * picheight) div 2);    {odd window dims mysteriously don't work}
  561.         if (bigpicwidth * bigpicheight) > UndoBufSize then begin
  562.                 PutError(concat('To register this stack, the size of the Undo buffer must be increased to at least ', long2str(bigpicwidth * bigpicheight div 1024), 'K.'));
  563.                 exit(DoRegister);
  564.             end;
  565.         xcenter := bigpicwidth div 2;                                              {find center}
  566.         ycenter := bigpicheight div 2;
  567. {open fiducial data file}
  568. {read fiducial marks and image centers into arrays; RegPic is image reference}
  569. {get screen to stage coordinate mapping}
  570.         case FiducialMethod of
  571.             OnScreen: 
  572.                 if not GetFiducialDataFromScreen(xcscreen, ycscreen, xscreen1, yscreen1, xscreen2, yscreen2, xstage1, ystage1, xstage2, ystage2, fiducials, xc, yc) then
  573.                     exit(DoRegister);
  574.             FromFile: 
  575.                 if not GetFiducialDataFromFile(xcscreen, ycscreen, xscreen1, yscreen1, xscreen2, yscreen2, xstage1, ystage1, xstage2, ystage2, fiducials, xc, yc) then
  576.                     exit(DoRegister)
  577.         end;     {case}
  578.         xxscale := (xscreen2 - xscreen1) / (xstage2 - xstage1);
  579.         yyscale := (yscreen2 - yscreen1) / (ystage2 - ystage1);
  580.         xcscreen := xcscreen + (bigpicwidth - picwidth) div 2;     {adjust for bigger window}
  581.         ycscreen := ycscreen + (bigpicheight - picheight) div 2;
  582.         xcenterStage := (xcenter - xcscreen) / xxscale;
  583.         ycenterStage := (ycenter - ycscreen) / yyscale;
  584.         UpdatePicWindow;
  585.         ShowWatch;
  586.         i := 1;
  587.         if not NewPicWindow('Temp', bigpicwidth, bigpicheight) then
  588.             exit(DoRegister);
  589.         ShowMessage(CmdPeriodToStop);
  590.         SlicesDone := 1; {Don't need to process reference slice}
  591.         while (i <= nImages) and not CommandPeriod do begin
  592.                 if i = RefImage then begin
  593.                         i := i + 1;
  594.                         if i > nImages then
  595.                             leave;
  596.                     end;
  597.                 StartTicks := TickCount;
  598.                 with info^ do
  599.                     SetWTitle(wptr, concat('Temp (', long2str(i), '/', long2str(nImages), ')'));
  600.         {rotate image then translate to complete registration}
  601.                 angle := RegisterFindAngle(fiducials, i, RefImage);
  602.                 if (angle > 9999) then
  603.                     leave;
  604.                 CenterInBigBuffer(i, picwidth, picheight, bigpicwidth, bigpicheight, StackInfo);
  605.                 if (abs(angle) > 0.0001) then
  606.                     RegisterRotate(angle);
  607.                 if CommandPeriod then
  608.                     leave;
  609.                 FindTranslation(xdelta, ydelta, fiducials, i, RefImage, xxscale, yyscale, angle, xc, yc, xcenterStage, ycenterStage);
  610.                 TranslateBackToStack(i, xdelta, ydelta, picwidth, picheight, bigpicwidth, bigpicheight, StackInfo);
  611.                 SlicesDone := SlicesDone + 1;
  612.                 TicksForOneRegistration := TickCount - StartTicks;
  613.                 TicksToGo := (nImages - SlicesDone) * TicksForOneRegistration;
  614.                 NumToString((TicksToGo div 60) mod 60, seconds);
  615.                 if length(seconds) = 1 then
  616.                     seconds := concat('0', seconds);
  617.                 timestr := concat(long2str(TicksToGo div 3600), ':', seconds);
  618.                 ShowMessage(concat(CmdPeriodToStop, crStr, 'time: ', timestr));
  619.                 i := i + 1;
  620.             end;     {while i}
  621.         Info^.changes := false;
  622.         ignore := CloseAWindow(info^.wptr);
  623.         info := StackInfo;
  624.         if CommandPeriod then
  625.             ShowMessage('Registration cancelled.')
  626.         else begin
  627.                 SetSlice(RefImage);  {select registered slice as current slice}
  628.                 UpdatePicWindow;
  629.                 info^.changes := true;
  630.                 ShowMessage('Registration successful.');
  631.             end;     {else}
  632.     end;
  633.  
  634.  
  635.  
  636. end.